package tech.yixiyun.framework.kuafu.db.sql.mysql;

import cn.hutool.core.util.ArrayUtil;
import tech.yixiyun.framework.kuafu.bean.annotation.ClassInitialize;
import tech.yixiyun.framework.kuafu.db.DbKit;
import tech.yixiyun.framework.kuafu.db.datasource.DbType;
import tech.yixiyun.framework.kuafu.db.session.DbSessionContext;
import tech.yixiyun.framework.kuafu.db.sql.*;
import tech.yixiyun.framework.kuafu.domain.ColumnDefinition;
import tech.yixiyun.framework.kuafu.domain.DomainContext;
import tech.yixiyun.framework.kuafu.domain.DomainDefinition;
import tech.yixiyun.framework.kuafu.domain.KeyDefinition;
import tech.yixiyun.framework.kuafu.domain.annotation.ColumnType;
import tech.yixiyun.framework.kuafu.domain.annotation.DomainType;
import tech.yixiyun.framework.kuafu.domain.annotation.GenerateFrom;
import tech.yixiyun.framework.kuafu.kits.StringKit;
import tech.yixiyun.framework.kuafu.kits.XSSKit;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.*;

/**
 * mysql数据库的语句拼接类
 *
 * @author Yixiyun
 * @version 1.0
 * @date 2021-05-07 14:51
 */
@ClassInitialize
public class Mysql extends Sql {

    static {
        Sql.registerSql(DbType.MYSQL, Mysql.class);
    }

    //部分语句
    private Map<Part, StringBuilder> partStatement;

    //部分参数
    private Map<Part, List<Serializable>> partArgs;




    @Override
    public Sql forUpdate() {
        setPartStatement(Part.FOR_UPDATE, new StringBuilder("for update"));
        return this;
    }

    @Override
    public Sql from(Class domainClass) {
        return from(StringKit.EMPTY, domainClass );
    }

    @Override
    public Sql from(String aliasName, Class domainClass) {
        this.domainClass = domainClass;
        return from(aliasName, DbKit.tableName(domainClass));
    }

    @Override
    public Sql from(Class domainClass, Serializable... args) {
        this.domainClass = domainClass;
        return from(DbKit.tableName(domainClass, args));
    }
    @Override
    public Sql from(String aliasName, Class domainClass, Serializable... args) {
        this.domainClass = domainClass;
        return from(aliasName, DbKit.tableName(domainClass, args));
    }

    @Override
    public Sql from(String tableName) {
        return fromMultiTable(tableName, StringKit.EMPTY);
    }

    @Override
    public Sql from(String aliasName, String tableName) {
        return fromMultiTable(tableName, aliasName);
    }

    @Override
    public Sql from(String aliasName, Sql table) {
        return fromMultiTable(table, aliasName);
    }

    @Override
    public Sql fromMultiTable(Serializable... args) {
        if (args.length % 2 != 0) {
            throw new SqlException("from方法传入的参数数量不正确，请检查");
        }
        StringBuilder stm = getPartStatement(Part.FROM, StringKit.EMPTY);
        clearPart(Part.FROM);

        for (int i = 0; i < args.length; i+=2) {
            if (i != 0) stm.append(",");
            if (args[i] instanceof CharSequence) {
                stm.append(args[i]);
            } else if (args[i] instanceof Sql) {
                Sql sql = (Sql) args[i];
                stm.append("(").append(sql.getStatement()).append(")");
                addPartArgs(Part.FROM, sql.getArgs(), false);
            } else {
                throw new SqlException("from 方法不支持 "+args[i].getClass()+"类型");
            }
            if (args[i+1] != StringKit.EMPTY) {
                stm.append(StringKit.SPACE).append(args[i + 1]);
            }

        }
        return this;
    }


    @Override
    public Sql where(CharSequence condition, Serializable... args) {
        StringBuilder stm = getPartStatement(Part.WHERE, StringKit.EMPTY);
        if (stm.length() > 0) {
            String link = getLink();
            if (link != null && link.length() > 0) {
                stm.append(StringKit.SPACE).append(link).append(StringKit.SPACE);
            }
        }
        stm.append(condition);
        addPartArgs(Part.WHERE, args, false);

        return this;
    }

    @Override
    public Sql bracketLeft() {
        StringBuilder stm = getPartStatement(Part.WHERE, StringKit.EMPTY);
        stm.append(BRACKET_LEFT);
        setLink(StringKit.EMPTY); // ( 后接的条件，不能用and或者or连接了
        return this;
    }

    @Override
    public Sql bracketRight() {
        StringBuilder stm = getPartStatement(Part.WHERE, StringKit.EMPTY);
        stm.append(BRACKET_RIGHT);
        setLink(AND);
        return this;
    }


    @Override
    public Sql eq(String column, Serializable value) {
        //TODO 最好统一处理下column，万一column中有%，应该替换成%%
        processCondition(column+"=%s", value);
        return this;
    }
    @Override
    public Sql ueq(String column, Serializable value) {
        processCondition(column+"!=%s", value);
        return this;
    }

    @Override
    public Sql gt(String column, Serializable value) {
        processCondition(column+">%s", value);
        return this;
    }
    @Override
    public Sql gte(String column, Serializable value) {
        processCondition(column+">=%s", value);
        return this;
    }
    @Override
    public Sql lt(String column, Serializable value) {
        processCondition(column+"<%s", value);
        return this;
    }
    @Override
    public Sql lte(String column, Serializable value) {
        processCondition(column+"<=%s", value);
        return this;
    }

    @Override
    public Sql isNull(String column) {
        where(column + " is null");
        return this;
    }

    @Override
    public Sql isNotNull(String column) {
        where(column + " is not null");
        return this;
    }

    @Override
    public Sql between(String column, Serializable startValue, Serializable endValue) {
        processCondition(column+" between %s and %s", startValue, endValue);
        return this;
    }

    @Override
    public Sql notBetween(String column, Serializable startValue, Serializable endValue) {
        return between(column + " not", startValue, endValue);
    }

    @Override
    public Sql like(String column, Serializable value) {
        where(column + " like ?", "%"+value+"%");
        return this;
    }

    @Override
    public Sql likeLeft(String column, Serializable value) {
        where(column + " like ?", value+"%");
        return this;
    }

    @Override
    public Sql likeRight(String column, Serializable value) {
        where(column + " like ?", "%"+value);
        return this;
    }

    @Override
    public Sql notLike(String column, Serializable value) {
        where(column + " not like ?", "%"+value+"%");
        return this;
    }

    @Override
    public Sql in(String column, Serializable... values) {
        StringBuilder stm = new StringBuilder(column);
        stm.append(" in (");
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                Serializable value = values[i];
                if (i != 0) stm.append(",");
                stm.append("?");
            }
        }
        stm.append(")");
        where(stm, (Serializable[]) values);
        return this;
    }


    @Override
    public Sql in(String column, Sql sql) {
        where(column + " in (" + sql.getStatement()+")", sql.getArgs());
        return this;
    }


    @Override
    public Sql in(String column, String sql) {
        where(column + " in (" + sql+")");
        return this;
    }

    @Override
    public Sql notIn(String column, Serializable... values) {
        return in(column + " not", values);
    }

    @Override
    public Sql notIn(String column, Sql sql) {
        return in(column + " not", sql);
    }

    @Override
    public Sql notIn(String column, String sql) {
        return in(column + " not", sql);
    }

    @Override
    public Sql exists(Sql sql) {
        where(" exists (" + sql.getStatement()+")", sql.getArgs());
        return this;
    }

    @Override
    public Sql notExists(Sql sql) {
        where(" not exists (" + sql.getStatement()+")", sql.getArgs());
        return this;
    }

    @Override
    public Sql group(String groupColumns) {
        StringBuilder stm = getPartStatement(Part.GROUP, StringKit.EMPTY);
        clearPart(Part.GROUP);
        stm.append(groupColumns);
        return this;
    }


    @Override
    public Sql having(String having) {
        StringBuilder stm = getPartStatement(Part.HAVING, StringKit.EMPTY);
        clearPart(Part.HAVING);
        stm.append(having);
        return this;
    }

    @Override
    public Sql order(String... orders) {
        if (orders == null) {
            return this;
        }
        if (orders.length % 2 != 0) {
            throw new SqlException("order方法必须按照: 列名、顺序、列名、顺序...的结构传入参数");
        }

        StringBuilder stm = getPartStatement(Part.ORDER, StringKit.EMPTY);
        clearPart(Part.ORDER);
        for (int i = 0; i < orders.length; i+=2) {
            if (i != 0) stm.append(",");
            stm.append(orders[i]).append(StringKit.SPACE).append(orders[i+1]);
        }
        return this;
    }

    @Override
    public Sql orderDesc(String column) {
        return this.order(column, "desc");
    }

    @Override
    public Sql orderAsc(String column) {
        return this.order(column, "asc");
    }

    @Override
    public Sql page(int page, int pageSize) {
        StringBuilder stm = getPartStatement(Part.PAGE, StringKit.EMPTY);
        clearPart(Part.PAGE);
        stm.append((page-1)*pageSize).append(",").append(pageSize);
        return this;
    }

    @Override
    public Sql page(int pageSize) {
        StringBuilder stm = getPartStatement(Part.PAGE, StringKit.EMPTY);
        clearPart(Part.PAGE);
        stm.append(pageSize);
        return this;
    }


    @Override
    public Sql leftJoin(String joinTable, String aliasName, String on) {
        StringBuilder stm = getPartStatement(Part.FROM, StringKit.EMPTY);
        stm.append(" left join ").append(joinTable).append(" ").append(aliasName).append(" on ").append(on);
        partStatement.put(Part.FROM, stm);
        return this;
    }

    @Override
    public Sql leftJoin(Sql joinTable, String aliasName, String on) {
        StringBuilder stm = getPartStatement(Part.FROM, StringKit.EMPTY);
        stm.append(" left join (").append(joinTable.getStatement()).append(") ").append(aliasName).append(" on ").append(on);
        addPartArgs(Part.FROM, joinTable.getArgs(), false);
        return this;
    }


    @Override
    public Sql union(Sql unionTable) {
        StringBuilder stm = getPartStatement(Part.UNION, StringKit.EMPTY);
        stm.append(" union ").append(unionTable.getStatement());
        addPartArgs(Part.UNION, unionTable.getArgs(), false);
        return this;
    }

    @Override
    public Sql union(String unionTable) {
        StringBuilder stm = getPartStatement(Part.UNION, StringKit.EMPTY);
        stm.append(" union ").append(unionTable);
        return this;
    }

    @Override
    public Sql unionAll(Sql unionTable) {
        StringBuilder stm = getPartStatement(Part.UNION, StringKit.EMPTY);
        stm.append(" union all ").append(unionTable.getStatement());
        addPartArgs(Part.UNION, unionTable.getArgs(), false);
        return this;
    }

    @Override
    public Sql unionAll(String unionTable) {
        StringBuilder stm = getPartStatement(Part.UNION, StringKit.EMPTY);
        stm.append(" union all ").append(unionTable);
        return this;
    }

    @Override
    public Sql set(boolean xss, Serializable... colAndValues) {
        if (colAndValues.length %2 != 0) {
            throw new SqlException("参数错误，set传入的数组中必须是column和value成对输入");
        }
        for (int i = 0; i < colAndValues.length; i+=2) {
            this.set(xss, (String) colAndValues[i], colAndValues[i+1]);
        }
        return this;
    }

    /**
     * update语句中的set部分
     * @param xss 是否需要对value做xss处理
     * @param column
     * @param value
     * @return
     */
    private Sql set(boolean xss, String column, Serializable value) {
        StringBuilder stm = getPartStatement(Part.SET, StringKit.EMPTY);
        if (stm.length() > 0) {
            stm.append(",");
        }
        if (value == null) {
            stm.append(column).append("=null");
        } else {
            if (value instanceof Exp) {
                stm.append(column).append("=").append(value.toString());
            } else if (value instanceof Sql) {
                Sql sql = (Sql) value;
                stm.append(column).append("=(").append(sql.getStatement()).append(")");
                addPartArgs(Part.SET, sql.getArgs(), false);
            } else {
                stm.append(column).append("=?");
                addPartArg(Part.SET, value, xss);
            }
        }
        partStatement.put(Part.SET, stm);
        return this;
    }

    @Override
    public Sql select(CharSequence columns) {
        this.select();

        StringBuilder stm = getPartStatement(Part.SELECT, StringKit.EMPTY);
        clearPart(Part.SELECT);
        stm.append(columns);
        return this;
    }

    @Override
    public Sql select(Serializable[] values) {
        this.select();

        StringBuilder stm = getPartStatement(Part.SELECT, StringKit.EMPTY);
        clearPart(Part.SELECT);
        for (int i = 0; i < values.length; i++) {
            if (i != 0) {
                stm.append(",");
            }
            if (values[i] instanceof Exp) {
                stm.append(values[i].toString());
            } else {
                stm.append("?");
                addPartArg(Part.SELECT, values[i], false);
            }
        }
        return this;
    }

    @Override
    public Sql selectWithout(CharSequence columns) {
        return selectWithout(columns, this.domainClass);
    }

    @Override
    public Sql selectWithout(CharSequence columns, Class domainClass) {
        if (domainClass == null) {
            throw new SqlException("未指定DomainClass，无法使用unselect");
        }

        String temp = StringKit.cleanBlank(columns.toString());
        if (StringKit.isBlank(temp)) {
            throw new SqlException("unselect columns 不能为空");
        }
        DomainDefinition definition = DomainContext.getDomainDefinition(domainClass);
        List<ColumnDefinition> allColumns = definition.getNormalColumnList();
        StringBuilder selectColumns = new StringBuilder();
        String[] unselectColumns = temp.split(",");
        int selectCount = 0;
        for (ColumnDefinition column : allColumns) {
            if (ArrayUtil.contains(unselectColumns, column.getName()) == false) {//不被排除
                if (selectCount > 0) {
                    selectColumns.append(",");
                }
                selectColumns.append(column.getName());
            }
            selectCount++;
        }
        select(selectColumns);
        return this;
    }

    /**
     * 建表语句
     * @return
     */
    public String getCreateTableStatement() {
        if (StringKit.isBlank(this.getTableName())) {
            throw new SqlException("未设置要创建的表的表名");
        }
        //表定义
        DomainDefinition definition = DomainContext.getDomainDefinition(domainClass);
        if (definition == null) {
            throw new SqlException(domainClass + "未注册为domain类，无法创建表");
        }
        if (definition.getType() != DomainType.TABLE) {
            throw new SqlException(domainClass + "映射的不是表结构，无法构建建表语句");
        }
        //列定义
        List<ColumnDefinition> columns = definition.getNormalColumnList();
        //索引定义
        List<KeyDefinition> keys = definition.getKeys();
        //CREATE TABLE `test2` (
        //  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT 'hello',
        //  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
        //  `sex` bit(1) NOT NULL,
        //  `salary` decimal(20,4) NOT NULL,
        //  `birthday` datetime(6) NOT NULL,
        //  `count` bigint(20) NOT NULL,
        //  `sn` int(10) unsigned zerofill DEFAULT NULL,
        //  `aaa` int(10) DEFAULT NULL,
        //  `bbb` varchar(20) NOT NULL DEFAULT '',
        //  PRIMARY KEY (`id`,`bbb`),
        //  UNIQUE KEY `def` (`id`),
        //  KEY `abc` (`count`,`sn`) USING BTREE COMMENT 'aaa',
        //  FULLTEXT KEY `ghi` (`bbb`)
        //) DEFAULT CHARSET=utf8mb4 COMMENT=' 啊啊啊啊';
        StringBuilder statement = new StringBuilder("create table ");
        statement.append("`").append(getTableName()).append("` (");
        int i = 0;
        for (ColumnDefinition col : columns) {
            if (i++ != 0) {
                statement.append(",");
            }
            statement.append("`").append(col.getName()).append("` ").append(col.getType().toString().toLowerCase());
            if (col.getType() == ColumnType.DECIMAL) {
                statement.append("(").append(col.getLength()).append(",").append(col.getPrecision()).append(")");
            } else if (col.getLength() > 0) {
                statement.append("(").append(col.getLength()).append(")");
            }
            if (col.getIsUnsigned()) {
                statement.append(" unsigned");
            }
            if (col.getIsZerofill()) {
                statement.append(" zerofill");
            }
            if (col.getIsNotNull()) {
                statement.append(" not null");
            }
            if (col.getIsPrimaryKey() && col.getGenerateFrom() == GenerateFrom.AUTOINCREMENT) {
                statement.append(" auto_increment");
            }
            String defaultValue = col.getDefaultValue();
            //如果默认值不是null 或者 默认值是null且字段未指定not null的话
            if (col.getIsPrimaryKey() == false && (("null".equals(defaultValue) && col.getIsNotNull() == false) || "null".equals(defaultValue) == false)) {
                statement.append(" default ").append(defaultValue);
            }
            if (StringKit.isNotBlank(col.getComment())) {
                statement.append(" comment '").append(col.getComment()).append("'");
            }
        }
        for (KeyDefinition key : keys) {
            statement.append(",").append(key.getType()).append(" key ");
            if (StringKit.isNotBlank(key.getName())) {
                statement.append("`").append(key.getName()).append("`");
            }
            statement.append(" (").append(StringKit.join( ",", (Serializable[]) key.getColumns()))
                    .append(")");
        }
        statement.append(")").append(" charset=").append(definition.getCharset());
        if (StringKit.isNotBlank(definition.getComment())) {
            statement.append(" comment='").append(definition.getComment()).append("'");
        }
        return statement.toString();
    }


    @Override
    public String getAlterTableStatement() {
        if (StringKit.isBlank(this.getTableName())) {
            throw new SqlException("未设置要表名");
        }
        //表定义
        DomainDefinition definition = DomainContext.getDomainDefinition(domainClass);
        if (definition == null) {
            throw new SqlException(domainClass + "未注册为domain类，无法创建表");
        }
        if (definition.getType() != DomainType.TABLE) {
            throw new SqlException(domainClass + "映射的不是表结构，无法构建改表语句");
        }
        //列定义
        List<ColumnDefinition> columns = definition.getNormalColumnList();
        //检查列是否已存在
        for (int i = 0; i < columns.size(); i++) {
            if (DbKit.columnExist(getDataSourceName(), getTableName().toString(), columns.get(i).getName())) {
                columns.remove(i--);
            }
        }
        //检查索引是否已存在
        List<KeyDefinition> keys = definition.getKeys();
        try {
            Connection connection = DbSessionContext.getConnection(getDataSourceName());
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet result = metaData.getIndexInfo(connection.getCatalog(), null, getTableName().toString(), false, false);
            while (result.next()) {
                String index_name = result.getString("INDEX_NAME");
                if (index_name.toLowerCase().equals("primary")) { //主键的键名为空字符串
                    index_name = StringKit.EMPTY;
                }
                for (int i = 0; i < keys.size(); i++) {
                    if (keys.get(i).getName().equalsIgnoreCase(index_name)) { //已存在
                        keys.remove(i--);
                    }

                }
            }
        } catch (Exception e) {
            throw new SqlException("获取表索引信息时发生异常", e);
        }

        if (columns.isEmpty() == false || keys.isEmpty() == false) { //有新增的列或者索引

            StringBuilder statement = new StringBuilder("alter table ");
            statement.append("`").append(this.getTableName()).append("` ");
            int i = 0;
            for (ColumnDefinition col : columns) {
                if (i++ != 0) {
                    statement.append(",");
                }
                statement.append("add column `").append(col.getName()).append("` ").append(col.getType().toString().toLowerCase());
                if (col.getType() == ColumnType.DECIMAL) {
                    statement.append("(").append(col.getLength()).append(",").append(col.getPrecision()).append(")");
                } else if (col.getLength() > 0) {
                    statement.append("(").append(col.getLength()).append(")");
                }
                if (col.getIsUnsigned()) {
                    statement.append(" unsigned");
                }
                if (col.getIsZerofill()) {
                    statement.append(" zerofill");
                }
                if (col.getIsNotNull()) {
                    statement.append(" not null");
                }
                if (col.getIsPrimaryKey() && col.getGenerateFrom() == GenerateFrom.AUTOINCREMENT) {
                    statement.append(" auto_increment");
                }
                String defaultValue = col.getDefaultValue();
                //如果默认值不是null 或者 默认值是null且字段未指定not null的话
                if (("null".equals(defaultValue) && col.getIsNotNull() == false) || "null".equals(defaultValue) == false) {
                    statement.append(" default ").append(defaultValue);
                }
                if (StringKit.isNotBlank(col.getComment())) {
                    statement.append(" comment '").append(col.getComment()).append("'");
                }

            }
            for (KeyDefinition key : keys) {
                if (i++ != 0) {
                    statement.append(",");
                }
                statement.append("add ").append(key.getType()).append(" key ");
                if (StringKit.isNotBlank(key.getName())) {
                    statement.append("`").append(key.getName()).append("`");
                }
                statement.append(" (").append(StringKit.join(",", (Serializable[]) key.getColumns()))
                        .append(")");
            }
            return statement.toString();
        }
        //不需要升表
        return null;
    }

    @Override
    public Sql setInsertColAndValues(String[] cols, Serializable[][] values) {
        if (cols.length == 0 || values.length == 0) {
            throw new SqlException("insert语句的col部分和value部分不能为空");
        }

        StringBuilder statement = new StringBuilder("insert into `").append(this.getTableName()).append("` (");
        for (int i = 0; i < cols.length; i++) {
            String col = cols[i];
            if (i != 0) {
                statement.append(",");
            }
            statement.append(col);
        }
        statement.append(") values ");
        for(int i = 0; i < values.length; i ++) {
            if (i == 0) {
                clearPart(Part.INSERT);
            }
            if (i != 0) {
                statement.append(",");
            }
            statement.append("(");
            Serializable[] value = values[i];
            if (value == null || value.length != cols.length) {
                throw new SqlException("传入的第"+(i+1)+"个values值数量和列数量不一致，请检查");
            }

            for (int j = 0; j < cols.length; j++) {

                String col = cols[j];
                if (j != 0) {
                    statement.append(",");
                }
                statement.append("?");
            }
            statement.append(")");
            addPartArgs(Part.INSERT, value, false);

        }
        setPartStatement(Part.INSERT, statement);



        return this;
    }


    @Override
    public Sql setInsertColAndValues(String[] cols, Sql querySql) {
        if (cols.length == 0 ) {
            throw new SqlException("insert语句的col部分不能为空");
        }
        if (querySql == null || querySql.getType() != SqlType.QUERY) {
            throw new SqlException("insert语句的querySql部分必须为一个查询语句");
        }
        StringBuilder statement = new StringBuilder("insert into `").append(this.getTableName()).append("` (");
        for (int i = 0; i < cols.length; i++) {
            String col = cols[i];
            if (i != 0) {
                statement.append(",");
            }
            statement.append(col);
        }
        statement.append(") ").append(querySql.getStatement());
        clearPart(Part.INSERT);
        setPartStatement(Part.INSERT, statement);
        addPartArgs(Part.INSERT, querySql.getArgs(), false);

        return this;
    }

    @Override
    public String getInsertStatement() {
        if (instance != null) {
            return getSaveStatementByInstance(true) ;
        }
        StringBuilder stmt = getPartStatement(Part.INSERT);
        if (stmt != null ) {
            return getInsertStatementByColsAndValues();
        }

        throw new SqlException("未指定实例或者列/值，无法创建插入语句");

    }


    /**
     * 根据指定的列和值，构建insert语句
     * @return
     */
    private String getInsertStatementByColsAndValues() {
        return partStatement.get(Part.INSERT).toString();
    }


    /**
     * 根据domain实例，构建save语句
     * @param onlyInsert 是否只处理插入
     * @return
     */
    private String getSaveStatementByInstance(boolean onlyInsert) {
        clearPart(Part.SET);
//        DomainDefinition definition = DomainContext.getDomainDefinition(instance.getClass());
//        if (definition.getType() != DomainType.TABLE) {
//            throw new SqlException(instance.getClass() + "不是表结构，无法执行新增");
//        }
//        List<ColumnDefinition> columns = definition.getNormalColumnList();

        List<DbKit.ColAndValue> list = DbKit.analyseDomainColAndValues(instance, true);
        StringBuilder statement = new StringBuilder("insert into `").append(this.getTableName()).append("` (");
        try {
            int i = 0; Serializable v = null;
            String[] nullColumns = instance.getNullColumns();
            for (DbKit.ColAndValue item : list) {
                if (i++ != 0) statement.append(",");
                statement.append("`").append(item.name).append("`");
                addPartArg(Part.INSERT, item.value, item.column.isXss());
                if (item.isPrimaryKey == false) {

                    if (onlyInsert == false ) {
                        if (ArrayUtil.contains(nullColumns, item.name)) {
                            set(item.column.isXss(), item.name, item.value); //指定null的进行更新
                        } else if(item.value != null && (item.value instanceof Exp  && ((Exp) item.value).getValue().equals("default")) == false) { //非null值的，进行更新
                            set(item.column.isXss(), item.name, Exp.new_("values("+item.name+")"));
                        }
                    }

                }
            }
            if (i == 0) {
                throw new SqlException("插入的实例所有字段都未指定值，插入失败");
            }
            statement.append(") values (");
            List<Serializable> args = getPartArgs(Part.INSERT);
            for (i = 0; i < args.size(); i++) {
                if (i != 0) statement.append(",");
                if (args.get(i) instanceof Exp) {
                    Serializable arg = args.remove(i--);
                    statement.append(arg);
                } else {
                    statement.append("?");
                }
            }
            statement.append(")");

            if (onlyInsert == false) {
                statement.append(" ON DUPLICATE KEY UPDATE ").append(getPartStatement(Part.SET));
            }

        } catch (Throwable e) {
            throw new SqlException("插入数据时发生异常", e);
        }

        return statement.toString();
    }

    @Override
    public String getSaveStatement() {
        if (instance == null) {
            throw new SqlException("未指定实例或者列/值，无法创建插入语句");
        }

        return getSaveStatementByInstance(false);
    }



    @Override
    public String getSelectStatement() {
        StringBuilder stm = new StringBuilder("select ");
        CharSequence temp = getPartStatement(Part.SELECT, null);
        stm.append(temp == null ? "*" : temp);
        temp = getPartStatement(Part.FROM, null);
        if (temp != null) {
            stm.append(" from ").append(temp);
        }
        temp = getPartStatement(Part.WHERE, null);
        if (temp != null) {
            stm.append(" where ").append(temp);
        }
        temp = getPartStatement(Part.GROUP, null);
        if (temp != null) {
            stm.append(" group by ").append(temp);
        }
        temp = getPartStatement(Part.HAVING, null);
        if (temp != null) {
            stm.append(" having ").append(temp);
        }
        temp = getPartStatement(Part.ORDER, null);
        if (temp != null) {
            stm.append(" order by ").append(temp);
        }
        temp = getPartStatement(Part.PAGE, null);
        if (temp != null) {
            stm.append(" limit ").append(temp);
        }
        temp = getPartStatement(Part.UNION, null);
        if (temp != null) {
            stm.append(StringKit.SPACE).append(temp);
        }

        temp = getPartStatement(Part.FOR_UPDATE, null);
        if (temp != null) {
            stm.append(StringKit.SPACE).append(temp);
        }

        return stm.toString();
    }


    @Override
    public String getDeleteStatement() {
        StringBuilder stm = new StringBuilder("delete from ").append(getTableName());
        CharSequence temp = getPartStatement(Part.WHERE, null);
        if (temp != null) {
            stm.append(" where ").append(temp);
        }
        temp = getPartStatement(Part.ORDER, null);
        if (temp != null) {
            stm.append(" order by ").append(temp);
        }
        temp = getPartStatement(Part.PAGE, null);
        if (temp != null) {
            stm.append(" limit ").append(temp);
        }
        return stm.toString();
    }

    @Override
    public String getUpdateStatement() {
        StringBuilder temp = getPartStatement(Part.SET, null);
        if (temp == null) {
            throw new SqlException("未设置更新的字段");
        }
        StringBuilder stm = new StringBuilder("update ").append(this.getTableName()).append(" set ").append(temp);
        temp = getPartStatement(Part.WHERE, null);
        if (temp != null) {
            stm.append(" where ").append(temp);
        }
        temp = getPartStatement(Part.ORDER, null);
        if (temp != null) {
            stm.append(" order by ").append(temp);
        }
        temp = getPartStatement(Part.PAGE, null);
        if (temp != null) {
            stm.append(" limit ").append(temp);
        }
        return stm.toString();
    }


    @Override
    public CharSequence getTableName() {
        if (partStatement == null) {
            return null;
        }
        return partStatement.get(Part.FROM);
    }

    /**
     * 清空Sql某部分的语句和参数
     * @param part
     */
    private void clearPart(Part part) {
        if (partArgs != null) {
            List<Serializable> list = partArgs.get(part);
            if (list != null) {
                list.clear();
            }
        }
        if (partStatement != null) {
            StringBuilder stm = partStatement.get(part);
            if (stm != null) {
                stm.delete(0, stm.length());
            }
        }
    }
    /**
     * 获取Sql某部分的语句，如果未设置过，就用默认值
     * @param part
     * @param defaultStatement
     * @return
     */
    private StringBuilder getPartStatement(Part part, String defaultStatement) {
        if (partStatement == null) {
            partStatement = new HashMap<>();
        }
        StringBuilder stm = partStatement.get(part);
        if (stm == null && defaultStatement != null) {
            stm = new StringBuilder(defaultStatement);
        }
        partStatement.put(part, stm);
        return stm;
    }
    /**
     * 获取Sql某部分的语句
     * @param part
     * @return
     */
    private StringBuilder getPartStatement(Part part) {
        if (partStatement == null) {
            return null;
        }
        return partStatement.get(part);
    }
    /**
     * 设置sql某部分的语句
     * @param part
     * @param statement
     */
    private void setPartStatement(Part part, StringBuilder statement) {
        if (partStatement == null) {
            partStatement = new HashMap<>();
        }
        partStatement.put(part, statement);
    }

    @Override
    public Serializable[] getArgs() {
        List<Serializable> args = null , temp = null;
        switch (this.type) {
            case QUERY:
                args = getPartArgs(new Part[]{Part.SELECT, Part.FROM, Part.WHERE, Part.GROUP, Part.ORDER, Part.PAGE, Part.UNION, Part.FOR_UPDATE});
                break;
            case INSERT:
                args = getPartArgs(Part.INSERT);
                break;
            case UPDATE:
                args = getPartArgs(new Part[]{Part.SET, Part.FROM, Part.WHERE, Part.ORDER, Part.PAGE});
                break;
            case SAVE:
                args = getPartArgs(Part.INSERT);
                break;
            case DELETE:
                args = getPartArgs(new Part[]{Part.FROM, Part.WHERE, Part.ORDER, Part.PAGE});
                break;
            case CREATE:
            case ALTER:
        }


        return args == null ? new Serializable[0] : args.toArray(new Serializable[0]);
    }


    /**
     * 为Sql某部分添加参数
     * @param part
     * @param arg
     * @param xss 是否需要做xss处理
     */
    private void addPartArg(Part part, Serializable arg, boolean xss) {
        if (partArgs == null) {
            partArgs = new HashMap<>();
        }
        arg = processArgValue(arg);

        if (xss && arg instanceof String) {
            arg = XSSKit.process((String) arg);
        }
        List<Serializable> list = partArgs.computeIfAbsent(part, k -> new LinkedList<>());
        list.add(arg);
    }

    /**
     * 为Sql某部分添加多个参数
     * @param part
     * @param args
     * @param xss
     */
    private void addPartArgs(Part part, Serializable[] args, boolean xss) {
        if (args == null) {
            return;
        }
        if (partArgs == null) {
            partArgs = new HashMap<>();
        }
        List<Serializable> list = partArgs.computeIfAbsent(part, k -> new LinkedList<>());
        for (int i = 0; i < args.length; i++) {
            Serializable arg = processArgValue(args[i]);
            if (xss && arg instanceof String) {
                arg = XSSKit.process((String) arg);
            }
            list.add(arg);
        }
    }

    /**
     * 获取Sql某部分的参数
     * @param part
     * @return
     */
    private List<Serializable> getPartArgs(Part part) {
        if (partArgs == null) {
            return null;
        }
        return partArgs.get(part);
    }

    /**
     * 获取Sql某几部分的参数
     * @param parts
     * @return
     */
    private List<Serializable> getPartArgs(Part[] parts) {
        List<Serializable> args = new ArrayList<>(), temp = null;
        for (Part part : parts) {
            temp = getPartArgs(part);
            if (temp != null) {
                args.addAll(temp);
            }
        }
        return args;
    }


    private void processCondition(CharSequence condition, Serializable... args) {

        if (args == null) {
            where(String.format(condition.toString(), "null"));
        } else {
            String[] strs = new String[args.length];
            Serializable[] finalArgs = null;
            for (int i = 0; i < args.length; i++) {
                Serializable arg = args[i];
                if (arg == null) {
                    strs[i] = "null";
                } else if (arg instanceof Exp) {
                    strs[i] = arg.toString();
                } else if (arg instanceof Sql) {
                    Sql sql = (Sql) arg;
                    strs[i] = "(" + sql.getStatement()+")";
                    finalArgs = finalArgs == null ? sql.getArgs() : ArrayUtil.addAll(finalArgs, sql.getArgs());
                } else {
                    strs[i] = "?";
                    if (arg.getClass().isEnum()) { //需要对枚举类做特殊处理
                        arg = arg.toString();
                    }
                    finalArgs = finalArgs == null ? new Serializable[]{arg} : ArrayUtil.append(finalArgs, arg);
                }
            }

            where(String.format(condition.toString(), (Serializable[]) strs), finalArgs);
        }
    }
}
